package org.anyline.simple.nebula;

import com.vesoft.nebula.client.graph.NebulaPoolConfig;
import com.vesoft.nebula.client.graph.SessionPool;
import com.vesoft.nebula.client.graph.SessionPoolConfig;
import com.vesoft.nebula.client.graph.data.HostAddress;
import com.vesoft.nebula.client.graph.data.ResultSet;
import com.vesoft.nebula.client.graph.exception.AuthFailedException;
import com.vesoft.nebula.client.graph.exception.BindSpaceFailedException;
import com.vesoft.nebula.client.graph.exception.ClientServerIncompatibleException;
import com.vesoft.nebula.client.graph.exception.IOErrorException;
import com.vesoft.nebula.client.graph.net.NebulaPool;
import com.vesoft.nebula.client.graph.net.Session;
import org.anyline.data.nebula.metadata.EdgeType;
import org.anyline.data.nebula.metadata.Tag;
import org.anyline.data.nebula.metadata.TagIndex;
import org.anyline.data.param.init.DefaultConfigStore;
import org.anyline.data.runtime.DataRuntime;
import org.anyline.data.runtime.RuntimeHolder;
import org.anyline.entity.DataSet;
import org.anyline.entity.graph.VertexRow;
import org.anyline.metadata.Column;
import org.anyline.metadata.Index;
import org.anyline.metadata.Table;
import org.anyline.proxy.ServiceProxy;
import org.anyline.service.AnylineService;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Lazy;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@SpringBootTest
public class NebulaTest {
    private static Logger log = LoggerFactory.getLogger(NebulaTest.class);
    @Lazy
    @Autowired
    private AnylineService service          ;
    //单机测试版本 安装  参考 http://qa.anyline.org/v/44_3932
    //如果提示-1005, host not enough  是因为没的执行 ADD HOSTS 127.0.0.1:9779

    public static void main(String[] args) {
        prepare();

        List<HostAddress> addresses = Arrays.asList(new HostAddress("localhost", 9669));
        String spaceName = "test";
        String user = "root";
        String password = "nebula";
        SessionPoolConfig sessionPoolConfig =
                new SessionPoolConfig(addresses, spaceName, user, password)
                        .setMaxSessionSize(10)
                        .setMinSessionSize(10)
                        .setWaitTime(100);
        SessionPool sessionPool = new SessionPool(sessionPoolConfig);
        if (!sessionPool.init()) {
            log.error("session pool init failed.");
            return;
        }
        ResultSet resultSet;
        try {
            resultSet = sessionPool.execute("match (v:player) return v limit 1;");
            System.out.println(resultSet.toString());
        } catch (IOErrorException | ClientServerIncompatibleException | AuthFailedException
                 | BindSpaceFailedException e) {
            e.printStackTrace();
            sessionPool.close();
            System.exit(1);
        }

        // execute in mutil-threads
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            executorService.submit(() -> {
                try {
                    ResultSet result = sessionPool.execute("match (v:player) return v limit 1");
                    System.out.println(result.toString());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        try {
            executorService.awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        executorService.shutdown();
        sessionPool.close();
    }


    private static void prepare() {
        NebulaPool pool = new NebulaPool();
        Session session;
        NebulaPoolConfig nebulaPoolConfig = new NebulaPoolConfig();
        nebulaPoolConfig.setMaxConnSize(100);
        List<HostAddress> addresses = Arrays.asList(new HostAddress("localhost", 9669));
        try {
            boolean initResult = pool.init(addresses, nebulaPoolConfig);
            if (!initResult) {
                log.error("pool init failed.");
                return;
            }

            session = pool.getSession("root", "nebula", false);
            ResultSet result =  session.execute("CREATE SPACE IF NOT EXISTS test(vid_type=fixed_string(20));"
                    + "USE test;"
                    + "CREATE TAG IF NOT EXISTS player(name string, age int);");
            int code = result.getErrorCode();
            String msg = result.getErrorMessage();
            System.out.println(msg);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        } finally {
            pool.close();
        }

    }
    @Test
    public void ddl_tag() throws Exception{
        Table table = service.metadata().table("HR_USER");
        if(null != table){
            //刚执行完crate不一定立即生效
            //service.ddl().drop(table);
        }
        table = new org.anyline.data.nebula.metadata.Tag("HR_USER");
        table.addColumn("user_name", "varchar");
        table.addColumn("age", "int");

        Index idx = new TagIndex("idx_age_name");
        idx.setComment("索引备注");
        //属性无效
        idx.addColumn("user_name");
        idx.addColumn("age");
        table.add(idx);
        //创建Tag
        service.ddl().create(table);
        service.ddl().drop(idx);

        System.out.println(service.querys("SHOW TAG INDEXES"));
        table = service.metadata().vertexTable("HR_USER");
        LinkedHashMap<String, Column> columns = table.getColumns();
        for(Column column:columns.values()){
            System.out.println("column:"+column.getName() + ", type:"+column.getFullType());
        }

    }

    @Test
    public void ddl_edge() throws Exception{
        Table table = service.metadata().table("HR_JOB");
        if(null != table){
            //刚执行完crate不一定立即生效
            service.ddl().drop(table);
        }
        table = new EdgeType("HR_JOB");
        table.addColumn("start_ymd","varchar(10)");
        table.addColumn("salary", "double");
        service.ddl().create(table);
        table = service.metadata().table("HR_JOB");
        LinkedHashMap<String, Column> columns = table.getColumns();
        for(Column column:columns.values()){
            System.out.println("column:"+column.getName() + ", type:"+column.getFullType());
        }
    }
    @Test
    public void dml_vertex() throws Exception{
        Table table = service.metadata().table("HR_USER");
        if (null == table){
            ddl_tag();
        }
        //刚执行完crate不一定立即生效
        //Thread.sleep(3000);

        //插入一点
        VertexRow row = new VertexRow();
        row.setPrimaryValue(100);
        row.put("user_name","zh100");
        row.put("age", 30);
        //根据table类型判断插入点还是边,如果table类型无法判断则根据row类型判断
        service.insert(table, row);

        service.insert(table, row, new DefaultConfigStore().override(false));
        //插入时判断是否存在，如果存在则不执行

        //插入多点
        DataSet set = new DataSet();
        for(int i=0; i<10; i++){
            row = new VertexRow();
            row.setPrimaryValue(i);
            row.put("user_name","zh"+i);
            row.put("age", i+10);
            set.add(row);
        }
        service.insert(table, set);

        //修改
        service.update(table, row, new DefaultConfigStore()
                .and("age", 10).and("user_name","zh")
        );
        //删除比较复杂 一般直接提供完整命令
        VertexRow edge = new VertexRow();
        edge.setPrimaryValue(10);
        service.delete(edge);
        service.execute(" DELETE VERTEX \"10\" WITH EDGE;");
        service.execute(" GO FROM \"10\" OVER HR_JOB WHERE properties(edge).age == 10 YIELD dst(edge) AS id | DELETE VERTEX $-.id;");
        set = service.querys("MATCH (v) RETURN v");
        System.out.println(set);
    }
    @Test
    public void dml_edge() throws Exception{
        //边的操作比较复杂 一般需要提供完成命令
    }
    @Test
    public void query() throws Exception{
            DataSet set = ServiceProxy.service("nebula").querys("MATCH (v:CRM_USER:HR_USER) RETURN v"
                    , new DefaultConfigStore()
                            .IGNORE_GRAPH_QUERY_RESULT_TOP_KEY(1)
                            //.IGNORE_GRAPH_QUERY_RESULT_TABLE(1)
                            .MERGE_GRAPH_QUERY_RESULT_TABLE(1)
            );
            System.out.println(set);

    }
    @Test
    public void help() throws Exception{

        //用这个org.anyline.data.nebula.metadata.Tag
        Tag table = new Tag("user");
        table.addColumn("ID", "int");
        table.addColumn("name", "varchar");
        service.ddl().create(table);

        Table tab = service.metadata().table("user");
        System.out.println(tab);
        service.execute("CREATE TAG IF NOT EXISTS CRM_USER(id INT NULL,name STRING NULL)");
        DataRuntime runtime = RuntimeHolder.runtime("nebula");
        SessionPool session = (SessionPool) runtime.getProcessor();
        ResultSet rs = session.execute(("CREATE TAG IF NOT EXISTS CRM_USER(id INT NULL,name STRING NULL)"));
        rs = session.execute(("CREATE TAG IF NOT EXISTS HR_USER(id INT NULL,name STRING NULL)"));
        System.out.println(rs.toString());
        System.out.println(rs.getErrorMessage());
        rs = session.execute("INSERT VERTEX CRM_USER (id, name) VALUES \"111\":(111,\"n111\"), \"222\":(222, \"n111\")");
        rs = session.execute("INSERT VERTEX CRM_USER (id, name) VALUES \"11\":(11,\"n11\"), \"22\":(22, \"n22\")");
        rs = session.execute("INSERT VERTEX CRM_USER (id, name) VALUES \"1\":(1,\"n1\"), \"2\":(2, \"n2\")");
        rs = session.execute("INSERT VERTEX HR_USER (id, name) VALUES \"111\":(111,\"n111\"), \"222\":(222, \"n111\")");
        rs = session.execute("INSERT VERTEX HR_USER (id, name) VALUES \"11\":(11,\"n11\"), \"22\":(22, \"n22\")");
        rs = session.execute("INSERT VERTEX HR_USER (id, name) VALUES \"1\":(1,\"n1\"), \"2\":(2, \"n2\")");
        System.out.println(rs.toString());
        System.out.println(rs.getErrorMessage());


    }
}
